//
// Descriptor.cs
//
// Author: 
//  Fredy Ramirez <framirez@pacificcoolwave.com>
//
// Copyright (C) 2011 Fredy Ramirez (http://www.pacificcoolwave.com)
//
//  Creation date: Dec 6, 2011
//  Last modification: 

/*
The MIT License

Copyright (C) 2011 Fredy Ramirez (http://www.pacificcoolwave.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 */


using System;
using System.Text.RegularExpressions;
using System.Xml;
using Gtk;

namespace Pcw.Addins.Highlighting.Definition
{
	internal class Descriptor
	{
		private static int id = 0;
		
		private int des_id; // id++ for each descriptor object
		private HighlightType hl_type;
		private Style style;
		private Regex regex;
		private string key;
		private string close_token;
		private bool multi_line;
		
		internal int Id {
			get {
				return des_id;
			}
		}
		
		internal HighlightType HlType {
			get {
				return hl_type;
			}
		}
		
		internal string Key {
			get{
				return key;
			}
		}
		
		internal string CloseToken {
			get{
				return close_token;
			}
		}
		
		internal bool IsMultiLine {
			get{
				return multi_line;
			}
		}
		
		internal string TagName {
			get{
				return style.TagName + "." + des_id;
			}
		}
		
		internal Descriptor (XmlNode descriptorNode, Style style, HighlightType type, bool caseSensitive)
		{
			Initialize(style, type, "", "", false);
			
			string regexString = "";			
			XmlNode tokensNode = descriptorNode.ChildNodes[0]; // Get tokens node
			
			foreach (XmlNode n in tokensNode.ChildNodes) 
			{				
				if (n.NodeType == XmlNodeType.Comment)
					continue;

				XmlElement itemNode = (XmlElement)n;
					
				regexString = regexString + "(" + 
						GetRegexString(XmlUtil.GetString(itemNode, "value"),XmlUtil.GetString(itemNode, "closeToken"), XmlUtil.GetString(itemNode, "escapeToken"), XmlUtil.GetBoolean(itemNode, "multiLine")) + 
						")|";

			}
			
			if (regexString.EndsWith("|"))
				regexString = regexString.Substring(0, regexString.Length - 1);
			
			regex = GetRegexObject(regexString, caseSensitive);
		}
		
		internal Descriptor (Style style, HighlightType type, 
						string keyToken, string closeToken, string escapeToken, 
						bool multiLine, bool caseSensitive)
		{
			Initialize(style, type, keyToken, closeToken, multiLine);

			string regexString = GetRegexString(keyToken, closeToken, escapeToken, multiLine);			
			regex = GetRegexObject(regexString, caseSensitive);

		}

		private void Initialize(Style style, HighlightType type, string keyToken, string closeToken, bool multiLine) 
		{
			id++;
			des_id = id;
			this.style = style;
			hl_type = type;
			key = keyToken;
			close_token = closeToken;
			multi_line = multiLine;
			
			if (type == HighlightType.ToCloseToken && closeToken == "")
				throw new ArgumentException("'closeToken must be assigned when type='ToCloseToken' in Pcw.Addins.Highlighting");			
		}
		
		internal TextTag GetTextTag()
		{
			Console.WriteLine("Creating tag with name: {0}", TagName);
			return style.GetTextTag(des_id);
		}

		internal Match[] GetMatches (string text)
		{
			if (regex.IsMatch(text)) 
				return FindSubstrings(text);
			
			return null;
		}
		
		private Match[] FindSubstrings(string source)
		{
			Match[] retArray = null;
			
			MatchCollection theMatches = regex.Matches(source);
	
			retArray = new Match[theMatches.Count];
			theMatches.CopyTo(retArray, 0);

			return retArray;
		}
		
		private Regex GetRegexObject(string stringPattern, bool caseSensitive)
		{
			RegexOptions options = new RegexOptions();
			options |= RegexOptions.Singleline;
			options |= RegexOptions.Multiline;
			if (!caseSensitive) options |= RegexOptions.IgnoreCase;
			
			regex = new Regex(stringPattern, options);
			
			Console.WriteLine("Regex object created with pattern: " + stringPattern);
			
			return regex;
		}

		private string GetRegexString(string token, string closeToken, string escapeToken, bool multiLine) 
		{
			string result = Regex.Escape(token);
			
			switch (hl_type)
			{
				case HighlightType.Contains:  // {value}
					break;
				case HighlightType.WholeWord: // \b{value}\b
					result = @"\b" + result + @"\b";
					break;
				case HighlightType.ToEOL:     // {value}[^\r\n]*$ - All line ^{value}[^\r\n]*$ - Starting with space ^\s*{value}[^\r\n]*$
					//result = result + @"[^\r\n]*$"; // TODO: Do not work with windwos file ending only with \r (must be tested)
					result = result + @"[^\r\n]*";
					break;
				case HighlightType.ToCloseToken:
					if (token.Length > 1 && multiLine)  // Is a block: {value}.*?{closeToken} 
						result = result + @".*?" + Regex.Escape(closeToken);
					else 
						result = GetToCloseTokenPattern(token, closeToken, escapeToken, multiLine);
					break;
			}
			
			return result;
			
		}
		
		private string GetToCloseTokenPattern(string token, string closeToken, string escapeToken, bool multiLine)
		{
			string pattern;
			
			if (escapeToken == "" && multiLine) 
				pattern = @"${START}[^${START}]*${CLOSE}";
			else if (escapeToken == "" && !multiLine)
				pattern = @"${START}[^${START}\r\n]*${CLOSE}";
			else if (escapeToken != "" && multiLine)
				pattern = @"${START}[^${CLOSE}${ESCAPE}]*(?:${ESCAPE}.[^${CLOSE}${ESCAPE}]*)*${CLOSE}";
			else if (escapeToken != "" && !multiLine)	
				pattern = @"${START}[^${CLOSE}${ESCAPE}\r\n]*(?:${ESCAPE}.[^${CLOSE}${ESCAPE}\r\n]*)*${CLOSE}";
		
			pattern = pattern.Replace("${START}", Regex.Escape(token));
			pattern = pattern.Replace("${CLOSE}", Regex.Escape(closeToken));
			pattern = pattern.Replace("${ESCAPE}", Regex.Escape(escapeToken));
			
			return pattern;
		}
	}
}

